#!/usr/bin/env python
#  -*- coding: utf-8 -*-

"""
    MCC 118 Functions Demonstrated:
        mcc118.a_in_scan_start
        mcc118.a_in_scan_read
        mcc118.a_in_scan_stop
        mcc118.a_in_scan_cleanup

    Purpose:
        Perform a continuous acquisition on 1 or more channels.

    Description:
        Continuously acquires blocks of analog input data for a
        user-specified group of channels until the acquisition is
        stopped by the user.  The last sample of data for each channel
        is displayed for each block of data received from the device.
        MCC118共8个通道，可以监测两个三相变压器的6路市电，分别为变压器1的A、B、C相和变压器2的A、B、C相
        5、6、19、16、20、21 LED使用的GPIO
        23 蜂鸣器
        8、9、10、11 SPI
        12、13、26 板子选择
        0、1 I2C EEPROM
        
"""
from __future__ import print_function
import threading
import numpy as np
import datetime
import time
from socket import *
import RPi.GPIO as GPIO
import sys
import os
import math
import signal

from ad7606 import ad7606, HatError
from led import LedCtl

# ===GPIO  setup=====================================================================
parameter = sys.argv                                  # 读取传入参数列表
GPIO.setmode(GPIO.BCM)
delta = datetime.timedelta(minutes=5)
mode = 0.0209  # 是根据电阻分压确定的3/220*1.414
ledctl = LedCtl()
#led_list = [2, 1, 0, 5, 4, 3]  # pin index of IO expand chip
led_list = [5, 4, 3, 2, 1, 0]  # pin index of IO expand chip
beep_pin=12
GPIO.setup(beep_pin, GPIO.OUT, initial=GPIO.LOW)
# ===================================================================================
READ_ALL_AVAILABLE = -1
CURSOR_BACK_2 = '\x1b[2D'
ERASE_TO_END_OF_LINE = '\x1b[0K'
data_path = "/home/pi/data"
os.chdir(data_path)
# =======OneNet param=================================================================
device_id = "77411"                             # Your 设备ID
api_key = "oQQbFdOlQRuhCwnMlBErfOfjgKEA"        # Your API_KEY
HOST = 'api.heclouds.com'
PORT = 80
BUFSIZ = 1024
ADDR = (HOST, PORT)
transno = ["Trans1A", "Trans1B", "Trans1C", "Trans2A", "Trans2B", "Trans2C"]       # 变压器的A、B、C相
fff = "dir-list"
# ==Use USB-disk=parameter setup=================================================================
param_path = os.path.normcase("/home/pi/config")            # 传入要读的文件路径
ds = list(os.walk(param_path))                                     # 获得所有文件夹的信息列表
for d in ds:
    fff = d[2]
param_path = param_path + "/param_conf.txt"
if "param_conf.txt" in fff:
    file = open(param_path, "r", encoding="utf-8", errors="ignore")
    for i in range(6):
        line = file.readline()
        if line.startswith('device_id:'):
            device_id = line[10:]
        if line.startswith('api_key:'):
            api_key = line[8:]
        if line.startswith('HOST:'):
            HOST = line[5:]
        if line.startswith('PORT:'):
            PORT = line[5:]
        if line.startswith('transno:'):
            transno_t = line[8:]
            transno = transno_t.split(',')
        if line.startswith('mode:'):
            if "PT" in line[5:]:
                mode = 0.014987                                # 9/220*1.414*(57/220)
# =======Channel set==================================================================
# Store the channels in a list and convert the list to a channel mask that
# can be passed as a parameter to the MCC 118 functions.
channels = [3, 4, 5]  #外壳丝印第一、二路与变压板1，2路反了，此处调整过来
channel_mask = channels
num_channels = len(channels)
samples_per_channel = 0
options = None  # OptionFlags.CONTINUOUS
scan_rate = int(3600.0)                        # 采样频率
read_request_size = int(3600)                  # 一次采集的点数
c_time = int(10)                                # 连续采样的次数
timeout = 10.0
# =======上下限=================================================================
up_volt = 264        # 根据需要设定上限电压
low_volt = 176       # 根据需要设定下限电压
#  ===============================  = 传入参数 ========parameter=sys.argv =======================================

if len(parameter[1]) > 7 and 3600 <= float(parameter[1][4:]) <= 7200:
    scan_rate = int(parameter[1][4:])
    print(scan_rate)

if len(parameter[2]) > 7 and 3600 <= float(parameter[2][4:]) <= 20000:
    read_request_size = int(parameter[2][4:])
    print(read_request_size)

if len(parameter) > 3 and len(parameter[3]) >= 6 and 240 <= float(parameter[3][4:]) <= 300:
    up_volt = float(parameter[3][4:])
    print(up_volt)

if len(parameter) > 4 and len(parameter[4]) >= 6 and 120 <= float(parameter[4][4:]) <= 200:
    low_volt = float(parameter[4][4:])
    print(low_volt)

print(scan_rate)
print(read_request_size)   # 每周期采样次数
print(up_volt)
print(low_volt)
samp_time = int(scan_rate/50)
# time.sleep(1000)

# =====================================================================================================================================
upline = up_volt*mode                               # 确定上限电压
downline = low_volt*mode                            # 确定下限电压
stand_line = 220*mode                               # 确定标准电压
alm_a_u = 3.9999 * stand_line * 1.2  # 确定上限面积
alm_a_d = 3.9999 * stand_line * 0.82  # 确定下限面积

# 定义一个采样缓存数组，一维长度为read_request_size*信道数，二维行数为连续采样的次数c_time
arraydata = [[0 for i in range(read_request_size*num_channels)] for h in range(c_time)]   
#  ============================================== LED监测 ====================================================
base_time = datetime.datetime.now()
led_stime = [base_time, base_time, base_time, base_time, base_time, base_time]      # 定义时间临时数组
led_status = [-1, -1, -1, -1, -1, -1]                                                    # 定义状态临时数组
transno = ["Trans1A", "Trans1B", "Trans1C", "Trans2A", "Trans2B", "Trans2C"]             # 变压器的A、B、C相
alm_sta = ["0", "100"]
running = True

# ==================================================================================================================================================
def led_detect():
    global ledctl, led_stime, led_status, transno
    try:
        # 判断每个LED的状态
        while PORT > 0 and running:
            time.sleep(1)

            if ledctl.ifon(led_list[0]) and led_status[0] < 0:
                led_stime[0] = datetime.datetime.now()
                led_status[0] = 1
            
            if ledctl.ifon(led_list[1]) and led_status[1] < 0:
                led_stime[1] = datetime.datetime.now()
                led_status[1] = 1
            
            if ledctl.ifon(led_list[2]) and led_status[2] < 0:
                led_stime[2] = datetime.datetime.now()
                led_status[2] = 1            

            # 相应引脚延时5分钟熄灭，并向平台发送数据清除告警
            for ii in range(3):
                now = datetime.datetime.now()
                if now > (led_stime[ii] + delta) and led_status[ii] >= 0:
                    ledctl.toggle(led_list[ii], False)
                    led_status[ii] = -1
                    t5 = threading.Thread(target=senddata, args=(transno[ii], alm_sta[0]))
                    t5.start()

            if led_status[0]+led_status[1]+led_status[2] < -2:
                GPIO.output(beep_pin, GPIO.LOW)
                     
    except KeyboardInterrupt:
        # Clear the '^C' from the display.
        print(CURSOR_BACK_2, ERASE_TO_END_OF_LINE, '\n')
        print('Stopping')


#  ===============================  = senddata to OneNet ====================================================
def senddata(t_dev, alm):
    Content = ""
    Content += "{\"datastreams\":[{\"id\": \"" + t_dev + "\",\"datapoints\": [{\"value\": " + alm + "}]}"
    Content += "]}\r\n"
    value = len(Content)
    data = ""
    data += "POST /devices/" + device_id + "/datapoints HTTP/1.1\r\n"
    data += "Host:api.heclouds.com\r\n"
    data += "Connection: close\r\n"
    data += "api-key:" + api_key + "\r\n"
    data += "Content-Length:" + str(value) + "\r\n"
    data += "\r\n"
    data += Content

    print(f'{t_dev}, {alm}')

    tcpCliSock = socket(AF_INET, SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    tcpCliSock.send(data.encode())
    # data1 = tcpCliSock.recv(BUFSIZ).decode()
    # print(data1)


#  =============================== writefile ================================================
def writefile(arrayatt, fnat):
    print("write     file    ", datetime.datetime.now())
    np.savetxt(fnat, arrayatt.T, fmt="%1.4f", delimiter=" ")
    print("\033[0;31m", "finish   write    ", datetime.datetime.now(), "\033[0m")


# ================  = Analyse and writefile and Alarm ========================================
def analyse(array, fnat):
    global ledctl
    break_1_1 = -1
    break_1_2 = -1
    break_1_3 = -1
    break_1 = -1

    print("analyse   file    ", datetime.datetime.now())
    file1 = "1-"+fnat
    a = np.array(array)
    bbb = np.empty([3, c_time * read_request_size], dtype=float)  # 变压器1数据定义一个临时数组
    bb = np.empty([3, c_time*read_request_size], dtype=float)  # 变压器1数据定义一个数组

    # 板子的数据记录格式是：CH0、CH1、CH2、CH3、CH4、CH5、CH0、CH1、CH2、CH3、CH4、CH5、CH0、CH1、CH2、CH3、CH4、CH5、CH0、CH1、CH2、CH3、CH4、CH5、CH0、CH1、CH2、CH3、CH4、CH5、
    # 通过下面拆分成6个CH每个CH一个list以便进行分CH判断
    bbb[0] = np.concatenate((a[0, 0::3], a[1, 0::3], a[2, 0::3], a[3, 0::3], a[4, 0::3], a[5, 0::3], a[6, 0::3],a[7, 0::3], a[8, 0::3], a[9, 0::3]))  
    bbb[1] = np.concatenate((a[0, 1::3], a[1, 1::3], a[2, 1::3], a[3, 1::3], a[4, 1::3], a[5, 1::3], a[6, 1::3],a[7, 1::3], a[8, 1::3], a[9, 1::3]))  
    bbb[2] = np.concatenate((a[0, 2::3], a[1, 2::3], a[2, 2::3], a[3, 2::3], a[4, 2::3], a[5, 2::3], a[6, 2::3],a[7, 2::3], a[8, 2::3], a[9, 2::3]))  
    
    bb[0]=bbb[0]-(np.amax(bbb[0]) +  np.amin(bbb[0]))/2  
    bb[1]=bbb[1]-(np.amax(bbb[1]) +  np.amin(bbb[1]))/2 
    bb[2]=bbb[2]-(np.amax(bbb[2]) +  np.amin(bbb[2]))/2 

# =============================== 判断越线 ================================================================================
    # points  是每周期点数   scan_rate = 10000.0  除以  50（交流电的频率）得出
    points = int((scan_rate/50))
    # cycle   是采了多少周期  read_request_size*c_time本次总的采样点数，除以每周期点数    
    cycle = int(read_request_size*c_time/points)

    for iii in range(cycle):
        # 每个CH拆分成每个正弦波周期进行峰值判断
        a1 = bb[0][(iii * points):((iii + 1) * points)]           # 一个完整波形使N+1个点，N个间隔（含头尾）
        b1 = bb[1][(iii * points):((iii + 1) * points)]           # 一个完整波形使N+1个点，N个间隔（含头尾）
        c1 = bb[2][(iii * points):((iii + 1) * points)]           # 一个完整波形使N+1个点，N个间隔（含头尾）

        # 计算面积
        area_a1 = 2 * math.pi * sum(abs(a1)) / points           # 计算每个波形的面积
        area_b1 = 2 * math.pi * sum(abs(b1)) / points           # 计算每个波形的面积
        area_c1 = 2 * math.pi * sum(abs(c1)) / points           # 计算每个波形的面积

        # 每一项分别判断上下限并分别告警
        # 第一个变压器A相判断上下限并分别告警
        if np.amax(abs(a1)) > upline or area_a1 > alm_a_u or area_a1 < alm_a_d:            #最大值大于上限，或面积大于上限，或面积小于下限
            if break_1_1 < 0:
                ledctl.toggle(led_list[0], True)  # 相应引脚置高电平，点亮LED
                break_1_1 = 5
                t3 = threading.Thread(target=senddata, args=(transno[0],alm_sta[1],))  #调用上传进程
                t3.start()
        # 第一个变压器B相判断上下限并分别告警
        if np.amax(abs(b1)) > upline or area_b1 > alm_a_u or area_b1 < alm_a_d:            #最大值大于上限，或面积大于上限，或面积小于下限
            if break_1_2 < 0:
                ledctl.toggle(led_list[1], True)  # 相应引脚置高电平，点亮LED
                break_1_2 = 5
                t3 = threading.Thread(target=senddata, args=(transno[1],alm_sta[1],))  #调用上传进程
                t3.start()
        # 第一个变压器C相判断上下限并分别告警
        if np.amax(abs(c1)) > upline or area_c1 > alm_a_u or area_c1 < alm_a_d:            #最大值大于上限，或面积大于上限，或面积小于下限
            if break_1_3 < 0:
                ledctl.toggle(led_list[2], True)  # 相应引脚置高电平，点亮LED
                break_1_3 = 5 
                t3 = threading.Thread(target=senddata, args=(transno[2],alm_sta[1],))  #调用上传进程
                t3.start()           

        #每个变压器任何一项有告警就调用写文件进程写文件                
        if break_1_1 > 0 or break_1_2 > 0 or break_1_3 > 0:
            if break_1 < 0:
                GPIO.output(beep_pin, GPIO.HIGH)  # 相应引脚置高电平，蜂鸣器响
                # t1 = threading.Thread(target=writefile, args=(bb, file1,))  #调用写文件进程
                # t1.start()
                writefile(bb, file1)  # 阻塞式写文件，避免写SD操作影响AD采样的SPI读取
                break_1 = 2

        if not running:
            GPIO.output(beep_pin, GPIO.LOW)
            break
    print("\033[0;32m","analyse finish    ", datetime.datetime.now(),"\033[0m")    #font color
#===============================================================================================================================
#======================主程序循环=================================================================================
#===============================================================================================================================


for i in range(6):
    senddata(transno[i],alm_sta[0])          #执行程序先清除平台告警

def handler(signum, frame):
    global running
    signame = signal.Signals(signum).name
    print(f'Signal handler called with signal {signame} ({signum})')
    print(CURSOR_BACK_2, ERASE_TO_END_OF_LINE, '\n')
    print('Stopping')
    ledctl.clear()
    GPIO.output(beep_pin, GPIO.LOW)
    running = False

signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

# ============================================================================================
try:
    ledctl.clear()
    GPIO.output(beep_pin, GPIO.LOW)
    t4 = threading.Thread(target=led_detect)      #调用告警检测进程实时监测告警状态，判断是否到时间消除告警
    t4.start()
      
    hat = ad7606()
    print('Starting scan ... Press Ctrl-C to stop\n')
    hat.a_in_scan_start(channel_mask, samples_per_channel, scan_rate,options)
    hat.a_in_scan_read(read_request_size * c_time, timeout * c_time)
    hat.a_in_scan_start(channel_mask, samples_per_channel, scan_rate, options)
    # =============数据采集===============================================================
    while running:
        now_time = datetime.datetime.now().strftime('%Y-%m-%d %H_%M_%S.%f')
        fna = str(now_time) + ".txt"
        # 连续采样10次
        timeout_happen = False
        print("sampled start: ", datetime.datetime.now())
        for i in range(c_time):
            read_result = hat.a_in_scan_read(read_request_size, timeout)  # read channel Data
            arraydata[i] = read_result.data

            if read_result.hardware_overrun:
                print('\n\nHardware overrun\n')
                break
            elif read_result.buffer_overrun:
                print('\n\nBuffer overrun\n')
                break
            elif read_result.timeout:
                print('\n\nSample timeout, maybe SPI bus is too busy\n')
                timeout_happen = True
                break

            hat.a_in_scan_stop()
            hat.a_in_scan_cleanup()
            hat.a_in_scan_start(channel_mask, samples_per_channel, scan_rate, options)
            if not running:
                break

        if timeout_happen:
            continue  # don't analyse, try sample again, will succeed when SD card read/write done

        print("sampled done: ", datetime.datetime.now())
        if not running:
            break

        # 调用分析进程
        #print(arraydata)
        arraydatat = arraydata
        t2 = threading.Thread(target=analyse, args=(arraydatat, fna,))
        t2.start()  # switch to another CPU core, avoid burning single core forever
        t2.join()   # wait until the analysis done, avoid SD card read/write interferes AD7606 sampling

    hat.a_in_scan_stop()
    hat.a_in_scan_cleanup()

except (HatError, ValueError) as err:
    print('\n', err)
